//Scala函数
object functionPractice {

  /*Scala函数
  Scala有函数和方法。
  Scala方法是一个具有名称和签名的类的一部分。 Scala中的函数是一个可以分配给变量的完整对象。
  函数定义可以出现在源文件中的任何位置。*/

  /*不带参数的函数
  在Scala中定义函数，请使用def关键字，后跟方法名和方法体，如下所示。*/
  def hellol() = {
    "hello world!"
  }

  /*等号=用作方法签名和方法体之间的分隔符。
  我们可以使用hello()或hello调用此函数。*/

  def main(args: Array[String]): Unit = {
    println(hellol())
  }

  //还可以包括可选的返回类型，如下所示。
  def hello1(): String = {
    "Hello World!"
  }

  //我们可以从方法体中完全删除括号。
  def hello2() = "Hello World!"

  //我们也可以从方法签名中删除括号。
  def hello3 = "Hello World!"

  /* 带参数的函数
   以下代码显示如何使用参数创建函数。*/
  def square(i: Int) = {
    i * i
  }

  /*函数的主体是表达式， 其中最后一行变为函数的返回值。
  我们可以调用这个函数为square（2）。*/
  object Main {
    def main(args: Array[String]) {
      def square(i: Int) = {
        i * i
      }

      println(square(2));
    }
  }

  /* 我们可以在函数中提供多个参数。多个参数之间用逗号分隔，如以下示例所示。
   def add(x: Int, y: Int): Int = { x + y }
   我们现在可以通过将实际参数传递给add函数来调用这个函数。*/
  object Main1 {
    def main(args: Array[String]) {
      def add(x: Int, y: Int): Int = {
        x + y
      }

      println(add(5, 5));
    }
  }

  /*Scala函数编程
  Scala允许我们将函数表达为文字。
  函数文字允许我们有一个函数类型的表达式，我们可以写一个短格式，而不声明它的名称。
  函数类型可以是以下之一：
  可以为其分配函数的变量或参数的类型
  采用函数参数的高阶函数的参数
  返回函数的高阶函数的结果类型
  函数常量以括号括起来的逗号分隔的参数列表开头，后跟箭头和函数体。
  函数常量也称为匿名函数。
  考虑一个add函数:*/
  val add = (x: Int, y: Int) => x + y
  //使用函数常量，您可以定义添加函数，如下所示：
  (x: Int, y: Int) => x + y

  /*一级函数和高阶函数
  Scala函数是对象。
  在函数式编程中，函数是一等公民函数。 一等公民函数可以
  分配给变量，
  作为参数传递给另一个函数
  作为其他函数的值返回。
  将函数作为参数或返回函数的函数称为高阶函数。*/

  /*函数作为变量
  正如我们可以传递String，Int和其他变量一样，我们可以像一个变量一样传递一个函数。
  我们可以定义一个函数常量，然后将该常量赋值给一个变量。
  以下代码定义了一个函数常量，它接受一个I​​nt参数，并返回一个值，该值是传递的Int的两倍：*/
  (i: Int) => {
    i * 2
  }
  //我们现在可以将该函数常量分配给一个变量：
  val doubler = (i: Int) => {
    i * 2
  }

  /*变量doubler是一个函数的实例，称为函数值。
  我们现在可以调用doubler，如下所示：
  doubler(2)
  doubler 是Function1 trait的一个实例，它定义了一个接受一个参数的函数。
  doubler 是使用关键字 val 创建并分配给变量的函数。
  函数作为参数
  我们可以创建一个函数或一个方法，它将一个函数作为参数。
  为此，首先定义一个将函数作为参数的方法。*/
  def operation(functionparam: (Int, Int) => Int) {
    println(functionparam(4, 4))
  }

  /*操作方法使用一个名为functionparam的参数，它是一个函数。
  functionparam函数接受两个Int并返回一个 Int 。
  操作方法返回一个Unit，指示操作方法不返回任何内容。
  接下来，定义一个与预期签名匹配的函数。以下add函数匹配该签名，因为它需要两个Int参数并返回Int：*/
  val add2 = (x: Int, y: Int) => {
    x + y
  }

  //现在我们可以将一个add函数传递给操作方法：
  object Main4 extends App {
    def operation(functionparam: (Int, Int) => Int) {
      println(functionparam(4, 4))
    }

    val add = (x: Int, y: Int) => {
      x + y
    }
    operation(add)
  }

  //任何匹配此签名的函数都可以传递到操作方法中。
  object Main2 extends App {
    def operation(functionparam: (Int, Int) => Int) {
      println(functionparam(4, 4))
    }

    val add = (x: Int, y: Int) => {
      x + y
    }
    operation(add)
    val subtract = (x: Int, y: Int) => {
      x - y
    }
    val multiply = (x: Int, y: Int) => {
      x * y
    }
    operation(subtract)
    operation(multiply)
  }

  /*返回函数
  我们可以从函数或方法返回一个函数。
  为了做到这一点，首先定义一个匿名函数。下面的代码声明一个匿名函数，它接受一个String参数并返回一个String：*/
  (name: String) => {
    "hello" + " " + name
  }

  //现在我们将定义一个方法来返回我们刚才定义的匿名函数。
  def greeting1() = (name: String) => {
    "hello" + " " + name
  }

  //在=符号的左侧有一个正常的方法声明：
  //def greeting()
  //在右边的是一个函数文字：
  def greeting3() = (name: String) => {
    "hello" + " " + name
  }

  //现在你可以将greeting()赋给一个变量:
  val greet = greeting3()

  //因为匿名函数接受一个String参数名，我们可以传递一个名字：
  object Main3 extends App {
    def greeting() = (name: String) => {
      "hello" + " " + name
    }

    val greet = greeting()
    println(greet("Scala"))
  }


}
